



function GetField(obj, fieldName)
	if not obj then
		return nil
	end

	if type(obj) == "userdata" then
		if obj:GetType() == typeof(FieldDictionary) then
			return obj:Get(fieldName)
		elseif typeof(ISerializableFieldContainer):IsAssignableFrom(obj:GetType()) then
			return ISerializableFieldContainer.GetFieldDictionary(obj):Get(fieldName)
		end
	end
	return obj[fieldName]
end

function GetTypeName(obj)
	if not obj then
		return
	end

	local fieldDictionary = nil
	if type(obj) == "userdata" then
		if not typeof(ISerializableFieldContainer):IsAssignableFrom(obj:GetType()) then
			return obj:GetType().FullName
		else
			fieldDictionary = ISerializableFieldContainer.GetFieldDictionary(obj)
		end
	elseif type(obj) == "table" then
		fieldDictionary = obj._fieldDictionary
	end

	if fieldDictionary then
		return fieldDictionary.className
	end
end

function InstantiateNew(obj)
	if not obj then
		return
	end

	if type(obj) == "table" then
		local v = newObject(obj._dataContainer)
		return CreateDataContainerProxy(v)
	end

	return newObject(obj)
end

function GetComponentNew(obj, classType)
	if not obj then
		return
	end

	if type(obj) == "table" then
		obj = obj._dataContainer
	end

	if not obj or obj:Equals(nil) then
		return
	end

	local v = obj:GetComponent(typeof(SerializeEx.ISerializableFieldContainer))
	if v ~= nil then
		if type(classType) == "string" then
			local fieldDictionary = ISerializableFieldContainer.GetFieldDictionary(v)
			if fieldDictionary.className ~= classType then
				local vs = v:GetComponentsEx(typeof(ISerializableFieldContainer))
				for i = 0, vs.Count - 1 do
					local v2 = vs[i]
					fieldDictionary = ISerializableFieldContainer.GetFieldDictionary(v2)
					if fieldDictionary.className == classType then
						v = v2
						break
					end
				end
				UnityListPools.Recycle(vs)
			end
		end
		
		v = CreateDataContainerProxy(v)
		return v
	end
	
	if classType ~= nil then
		return obj:GetComponent(typeof(classType))
	end
end


local DataContainerProxy = {}



function DataContainerProxy.__index(t, k)
	local v, hasKey = t._fieldDictionary:Get(k, nil)
	if type(v) == "userdata" then
		if v:GetType() == typeof(SerializeEx.FieldDictionary) then
			v = CreateDataContainerProxy(v)
		elseif typeof(SerializeEx.ISerializableFieldContainer):IsAssignableFrom(v:GetType()) then
			v = CreateDataContainerProxy(v)
		end

		if v ~= nil then
			rawset(t, k, v)
			return v
		end
	end

	if v == nil and not hasKey then
		v = DataContainerProxy[k]
		if not v then
			local dataContainer = rawget(t, "_dataContainer")
			if dataContainer and not dataContainer:Equals(nil) then
				return dataContainer[k]
			end
		end
	end

	return v
end

function DataContainerProxy.__newindex(t, k, v)
	rawset(t, k, v)
	
	-- t._fieldDictionary:Set(k, v)
end

-- function DataContainerProxy.__eq(a, b)
-- 	if rawequal(a, b) then
-- 		return true
-- 	elseif a._dataContainer == b then
-- 		return true
-- 	elseif a._fieldDictionary == b then
-- 		return true
-- 	end

-- 	return false
-- end

function DataContainerProxy:GetComponent(t)
	return self.gameObject:GetComponent(t)
end

function DataContainerProxy:GetComponentInChildren(t, includeInactive)
	return self.gameObject:GetComponentInChildren(t, includeInactive)
end

function DataContainerProxy:GetComponentInParent(t)
	return self.gameObject:GetComponentInParent(t)
end

function DataContainerProxy:GetComponents(t, result)
	return self.gameObject:GetComponents(t, result)
end

function DataContainerProxy:GetComponentsInChildren(t, includeInactive)
	return self.gameObject:GetComponentsInChildren(t, includeInactive)
end

function DataContainerProxy:GetComponentsInParent(t, includeInactive)
	return self.gameObject:GetComponentsInParent(t, includeInactive)
end

function DataContainerProxy:Equals(v)
	if not self then
		return self == v
	end
	
	local obj = self.gameObject

	if not obj then
		return obj == v
	end
	
	return obj:Equals(v)
end


local fieldDictionaryTableMap = {}
setmetatable(fieldDictionaryTableMap, {__mode='kv'})


function CreateDataContainerProxy(obj)
	if not obj then
		return obj
	end

	local fieldDictionary = nil
	local dataContainer = nil
	if type(obj) == "userdata" then
		if obj:GetType() == typeof(SerializeEx.FieldDictionary) then
			fieldDictionary = obj
		elseif typeof(SerializeEx.ISerializableFieldContainer):IsAssignableFrom(obj:GetType()) then
			dataContainer = obj
			fieldDictionary = SerializeEx.ISerializableFieldContainer.GetFieldDictionary(obj)
		else
			return obj
		end
	else
		return obj
	end

	local mode = 2
	if mode == 1 then
		if fieldDictionary.userData then
			return fieldDictionary.userData
		end
	elseif mode == 2 then
		if fieldDictionaryTableMap[fieldDictionary] then
			return fieldDictionaryTableMap[fieldDictionary]
		end
	end

	local me = fieldDictionary.this
	if not me._fieldDictionary then
		me._fieldDictionary = fieldDictionary
		me._dataContainer = dataContainer
		setmetatable(me, DataContainerProxy)
		if mode == 1 then
			fieldDictionary.userData = me
		end

		if mode == 2 then
			fieldDictionaryTableMap[fieldDictionary] = me
		end
	end
	return me
end

function CreateBehaviourProxy(ctrlName, dataObj, funObj)
	local ctrl = _G[ctrlName]
	local me = {_ctrl = ctrl, _dataObj = dataObj, _funObj = funObj}
	me = New(ctrl, me)
	return me
end
